package org.finalministry.importexport;

import geodb.GeoDB;

import java.io.File;
import java.sql.SQLException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.List;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;

import org.finalministry.database.DatabaseFactory;
import org.finalministry.database.dao.DaoFactory;
import org.finalministry.objects.Area;
import org.finalministry.objects.Congregation;
import org.finalministry.objects.Contact;
import org.finalministry.objects.FlagsTagsNotes;
import org.finalministry.objects.Territory;
import org.finalministry.objects.address.Address;
import org.finalministry.objects.address.Country;
import org.finalministry.utils.geo.GeoUtils;
import org.finalministry.xml.AddressType;
import org.finalministry.xml.AreaType;
import org.finalministry.xml.CongregationType;
import org.finalministry.xml.ContactType;
import org.finalministry.xml.CountryType;
import org.finalministry.xml.FinalministryType;
import org.finalministry.xml.FlagsTagsNotesEnumType;
import org.finalministry.xml.FlagsTagsNotesType;
import org.finalministry.xml.GeometryType;
import org.finalministry.xml.ObjectFactory;
import org.finalministry.xml.TerritoryType;

import com.vividsolutions.jts.geom.Geometry;

/**
 * TODO: Processing und Registered ebenfalls exportieren und importieren<br>
 * TODO: IMPORT in die Datenbank
 * 
 * @author admin
 * 
 */
public class FinalministryDataImportExport {

	private ObjectFactory objectFactory;
	private FinalministryType finalministry;
	private final SimpleDateFormat sdf = new SimpleDateFormat("dd.MM.yyyy HH:mm");

	public FinalministryDataImportExport() {
		objectFactory = new ObjectFactory();
		finalministry = objectFactory.createFinalministryType();
	}

	public void exportCongregation(File exportFile, Long congregationID, String note) throws Exception {

		finalministry.setAuthor("Peter");
		finalministry.setExportTime(sdf.format(Calendar.getInstance().getTime()));
		finalministry.setNote(note);

		CongregationType congregationT = makeCongregationType(congregationID);

		finalministry.getCongregations().add(congregationT);

		marshal(exportFile);
	}

	public void marshal(File exportFile) {
		try {

			JAXBElement<FinalministryType> root = objectFactory.createFinalministry(finalministry);

			JAXBContext jaxbContext = JAXBContext.newInstance("org.finalministry.xml"); // Finalministry.class);
			Marshaller jaxbMarshaller = jaxbContext.createMarshaller();

			jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);

			// Marshal the employees list in console
			jaxbMarshaller.marshal(root, System.out);

			// Marshal the employees list in file
			if (exportFile != null)
				jaxbMarshaller.marshal(root, exportFile);
		} catch (JAXBException jbe) {
			jbe.printStackTrace();
		}
	}

	public CongregationType makeCongregationType(Long congregationID) throws Exception {

		Congregation congregation = DatabaseFactory.getCongregationDAO().find(congregationID);

		if (congregation == null) {
			System.err.println("No congregation with id " + congregationID + " found!");
			return null;
		}

		CongregationType congregationT = objectFactory.createCongregationType();
		congregationT.setName(congregation.getName());

		CountryType language = objectFactory.createCountryType();
		language.setIsocode3(congregation.getLanguage().getCountryIsoCode3());

		congregationT.setLanguage(language);

		Address address = congregation.getAddress();

		AddressType addressT = makeAddressType(address);

		congregationT.setAddress(addressT);

		DatabaseFactory.setCongregationID(congregationID);
		List<Territory> territoryList = DatabaseFactory.getTerritoryDAO().findAll();

		makeTerritoryTypeForCongregation(congregationT, territoryList);

		return congregationT;
	}

	public AddressType makeAddressType(Address address) {

		AddressType addressT = objectFactory.createAddressType();
		addressT.setStreet(address.getStreet());
		addressT.setHousenumber(address.getHousenumber());
		addressT.setPostalcode(address.getPostalcode());
		addressT.setCity(address.getCity());

		Country country = address.getCountry();
		CountryType countryT = makeCountryType(country);
		addressT.setCountry(countryT);

		addressT.setCoordinate(getGeometryType(address.getCoordinate(), GeoUtils.NONSPHERICAL_MERCATOR_4326));

		return addressT;
	}

	public CountryType makeCountryType(Country country) {
		CountryType countryT = objectFactory.createCountryType();
		countryT.setIsocode3(country.getCountryIsoCode3());
		return countryT;
	}

	public void makeTerritoryTypeForCongregation(CongregationType congregationT, List<Territory> territoryList) throws Exception {
		for (Territory territory : territoryList) {

			TerritoryType territoryT = objectFactory.createTerritoryType();
			territoryT.setNumber(territory.getNumber());
			territoryT.setCity(territory.getCity());
			territoryT.setPostalcode(territory.getPostalcode());
			territoryT.setPreRenderedMapImagePathOrUrl(getPrerenderedPathToImage(territory));

			if (territory.getArea() != null) {
				territoryT.setArea(getAreaFromTerritory(territory));
			}

			List<Contact> contactList = DatabaseFactory.getContactDAO().getAllByTerritoryID(territory.getId());

			makeContactTypeForTerritory(territoryT, contactList);

			congregationT.getTerritory().add(territoryT);
		}
	}

	public void makeContactTypeForTerritory(TerritoryType territoryT, List<Contact> contactList) throws ClassNotFoundException, SQLException,
			Exception {
		for (Contact contact : contactList) {
			ContactType contactT = objectFactory.createContactType();
			contactT.setLastname(contact.getLastName());
			contactT.setAddress(makeAddressType(contact.getAddress()));

			// DatabaseFactory.getFlagsTagsNotesDAO().refreshTagsForObject(contact);

			List<FlagsTagsNotes> flags = contact.getFlags();
			List<FlagsTagsNotes> tags = contact.getTags();
			List<FlagsTagsNotes> notes = contact.getNotes();

			makeFlagsTagsNotesTypeForContact(contactT, flags, FlagsTagsNotesEnumType.FLAG);
			makeFlagsTagsNotesTypeForContact(contactT, tags, FlagsTagsNotesEnumType.TAG);
			makeFlagsTagsNotesTypeForContact(contactT, notes, FlagsTagsNotesEnumType.NOTE);

			territoryT.getContact().add(contactT);
		}
	}

	public void makeFlagsTagsNotesTypeForContact(ContactType contactT, List<FlagsTagsNotes> flags, FlagsTagsNotesEnumType flagsTagsNotesEnum) {

		for (FlagsTagsNotes flag : flags) {

			FlagsTagsNotesType flagT = objectFactory.createFlagsTagsNotesType();
			flagT.setFlagsTagsNotesEnum(flagsTagsNotesEnum);
			flagT.setName(flag.getName());
			flagT.setText(flag.getText());

			contactT.getFlag().add(flagT);
		}
	}

	private AreaType getAreaFromTerritory(Territory territory) {

		AreaType areaT = objectFactory.createAreaType();
		areaT.setName(territory.getArea().getName());
		areaT.setDescription(territory.getArea().getDescription());
		areaT.setGeometry(getGeometryType(territory.getArea().getGeometry(), GeoUtils.NONSPHERICAL_MERCATOR_4326));
		return areaT;
	}

	private GeometryType getGeometryType(Geometry geometry, int code) {

		String wellKnownText = GeoDB.gToWKT(geometry);
		GeometryType geometryT = objectFactory.createGeometryType();
		geometryT.setCode(String.valueOf(code));
		geometryT.setWellKnownText(wellKnownText);
		return geometryT;
	}

	private String getPrerenderedPathToImage(Territory territory) {
		String path = "map_" + territory.getId() + ".jpg";
		return path;
	}

	public void importCongregation(File importFile) throws Exception {
		FinalministryType fm = unmarshal(importFile);

		for (CongregationType c : fm.getCongregations()) {

			Congregation congregation = new Congregation();
			congregation.setName(c.getName());
			congregation.setLanguage(DatabaseFactory.getCountryDAO().getByIsoCode3(c.getLanguage().getIsocode3()));
			congregation.setAddress(extractAddressFromType(c.getAddress()));

			congregation = DatabaseFactory.getCongregationDAO().merge(congregation);

			DatabaseFactory.setCongregationID(congregation.getId());
			extractTerritoriesFromCongregationType(c, congregation.getId());
		}
	}

	private void extractTerritoriesFromCongregationType(CongregationType c, Long congregationID) throws Exception {

		for (TerritoryType t : c.getTerritory()) {

			Territory territory = new Territory();
			territory.setCongregationID(congregationID);
			territory.setNumber(t.getNumber());
			territory.setCity(t.getCity());
			territory.setPostalcode(t.getPostalcode());
			territory.setArea(extractAreaFromType(t.getArea()));

			territory = DatabaseFactory.getTerritoryDAO().merge(territory);

			extractContactsFromTerritoryType(territory.getId(), t);
		}

	}

	private void extractContactsFromTerritoryType(Long territoryID, TerritoryType t) throws Exception {

		for (ContactType c : t.getContact()) {

			Contact contact = new Contact();
			contact.setLastName(c.getLastname());
			contact.setAddress(extractAddressFromType(c.getAddress()));
			contact.setTerritoryID(territoryID);
			contact.setCongregationID(DatabaseFactory.getCongregationID());

			if (c.getLanguage() != null && c.getLanguage().getIsocode3() != null) {
				contact.setLanguage(DatabaseFactory.getCountryDAO().getByIsoCode3(c.getLanguage().getIsocode3()));
			}

			contact = DatabaseFactory.getContactDAO().merge(contact);

			if (c.getFlag() != null) {

				for (FlagsTagsNotesType ftn : c.getFlag()) {
					if (ftn.getFlagsTagsNotesEnum().equals(FlagsTagsNotesEnumType.FLAG) && ftn.getName() != null) {

						DaoFactory.getInstance().getFlagsTagsNotesDAO().flagContact(contact, ftn.getName());
					}
				}
			}

			DaoFactory.getInstance().getContactDAO().initContactTypeFlags(contact);

		}

	}

	private Area extractAreaFromType(AreaType a) throws Exception {

		Area area = new Area();
		area.setName(a.getName());
		area.setDescription(a.getDescription());
		area.setCongregationID(DaoFactory.getInstance().getCongregationID());
		area.setGeometry(GeoUtils.getGeometryCoordinateFromText(a.getGeometry().getWellKnownText()));
		Long id = DaoFactory.getInstance().getAreaDAO().create(area);
		area.setId(id);

		return area;
	}

	private Address extractAddressFromType(AddressType a) throws Exception {

		Address address = new Address();
		address.setStreet(a.getStreet());
		address.setHousenumber(a.getHousenumber());
		address.setPostalcode(a.getPostalcode());
		address.setCity(a.getCity());
		address.setCreated(Calendar.getInstance());

		address.setCountry(DaoFactory.getInstance().getCountryDAO().getByIsoCode3(a.getCountry().getIsocode3()));

		if (a.getCoordinate() != null && a.getCoordinate().getWellKnownText().equals("POINT ()") == false
				&& a.getCoordinate().getCode().equals(String.valueOf(GeoUtils.SPHERICAL_MERCATOR_900913))) {
			address.setCoordinate(GeoUtils.getNonSphericalFromSpherical(a.getCoordinate().getWellKnownText()));
		}

		if (a.getCoordinate() != null && a.getCoordinate().getWellKnownText().equals("POINT ()") == false
				&& a.getCoordinate().getCode().equals(String.valueOf(GeoUtils.NONSPHERICAL_MERCATOR_4326))) {
			address.setCoordinate(GeoUtils.getGeometryCoordinateFromText(a.getCoordinate().getWellKnownText()));
		}

		Long id = DaoFactory.getInstance().getAddressDAO().create(address);
		address.setId(id);

		return address;
	}

	@SuppressWarnings("unchecked")
	public FinalministryType unmarshal(File importFile) throws JAXBException {

		JAXBContext jaxbContext = JAXBContext.newInstance("org.finalministry.xml");
		Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();

		return ((JAXBElement<FinalministryType>) jaxbUnmarshaller.unmarshal(importFile)).getValue();
	}
}
